#include <zenilib.h>
#include "LevelEditor.h"
#include "Globals.h"
#include "Civilian.h"
#include "Boss.h"
#include "Platform.h"
#include <iostream>

using namespace Zeni;

LevelEditor::LevelEditor(int levelNum) : 
	timeSinceMove(0),
	draw("Platform"),
	controlHeld(false),
	panning(false),
	moveObject(NULL),
	movingPlayer(NULL),
	newPlatform(NULL),
	mousePrev(Vector2(0, 0)),
	mousePrev2f(Point2f(0, 0)),
	ground(NULL)
{
	if (levelNum == -1){
		player = new Player(Vector2(50, 500));
		filename = "levels/LevelTemp.txt";
		levelName = "LevelTemp";
	}
	else{
		ifstream iFile;
		filename = "levels/Level" + itos(levelNum) + ".txt";
		iFile.open(filename.c_str());
		iFile >> levelName;

		float x, y, width, height;
		string texture, heading;
		iFile >> heading >> x >> y;
		player = new Player(Vector2(x, y));

		while (!iFile.eof()){
			iFile >> heading;
			if (heading == "Platform"){
				iFile >> x >> y >> width >> height >> texture;
				platforms.push_back(new Platform(Vector2(x, y), Vector2(width, height), texture));
			}
			else if (heading == "Civilian"){
				iFile >> x >> y;
				npcs.push_back(new Civilian(Vector2(x, y)));
			}
			else if (heading == "Enemy"){
				iFile >> x >> y;
				npcs.push_back(new Enemy(Vector2(x, y)));
			}
			else if (heading == "Boss"){
				iFile >> x >> y;
				npcs.push_back(new Boss(Vector2(x, y)));
			}
			else if (heading == "HealthPack") {
				iFile >> x >> y;
				healthPacks.push_back(new HealthPack(Vector2(x, y)));
			}
			else{
				break;
			}
		}
		iFile.close();
	}

	viewArea.first = Point2f(0, 0);
	viewArea.second = Point2f(screenWidth, screenHeight);
	determineBackground();
}

LevelEditor::~LevelEditor()
{
	save();
	for (int i = 0; i < platforms.size(); i++){
		delete platforms[i];
	}
	platforms.clear();

	for (int i = 0; i < npcs.size(); i++) {
		delete npcs[i];
	}
	npcs.clear();

	delete player;
	delete ground;
}

void LevelEditor::save()
{
	ofstream oFile;
	oFile.open(filename.c_str());
	oFile << levelName << endl;
	oFile << "Player " << player->getPos().x << " " << player->getPos().y << endl;
	for (int i = 0; i < platforms.size(); i++){
		oFile << "Platform " << platforms[i]->getPos().x << " " << 
			     platforms[i]->getPos().y << " " <<
				 platforms[i]->getScale().x << " " <<
				 platforms[i]->getScale().y << " " <<
				 platforms[i]->getTexture(0) << endl;
	}
	for (int i = 0; i < npcs.size(); i++) {
		if (npcs[i]->playertype == CIVILIAN){
			oFile << "Civilian ";
		}
		else if (npcs[i]->playertype == ENEMY){
			oFile << "Enemy ";
		}
		else if (npcs[i]->playertype == BOSS){
			oFile << "Boss ";
		}
		oFile << npcs[i]->getPos().x << " " << npcs[i]->getPos().y << endl;
	}
	for (int i = 0; i < healthPacks.size(); i++) {
		oFile << "HealthPack " << healthPacks[i]->getPos().x << " "
					<< healthPacks[i]->getPos().y << endl;
	}
	oFile << "End";
	oFile.close();
}

void LevelEditor::determineBackground()
{
	float minX = player->getBody()->left();
	float maxX = player->getBody()->right();
	float minY = player->getBody()->top();
	float maxY = player->getBody()->bottom();

	for (int i = 0; i < platforms.size(); i++) {
		if (platforms[i]->left() < minX){
			minX = platforms[i]->left();
		}
		if (platforms[i]->right() > maxX){
			maxX = platforms[i]->right();
		}
		if (platforms[i]->top() < minY){
			minY = platforms[i]->top();
		}
		if (platforms[i]->bottom() > maxY){
			maxY = platforms[i]->bottom();
		}
	} 
	for (int i = 0; i < npcs.size(); i++) {
		if (npcs[i]->getBody()->left() < minX){
			minX = npcs[i]->getBody()->left();
		}
		if (npcs[i]->getBody()->right() > maxX){
			maxX = npcs[i]->getBody()->right();
		}
		if (npcs[i]->getBody()->top() < minY){
			minY = npcs[i]->getBody()->top();
		}
		if (npcs[i]->getBody()->bottom() > maxY){
			maxY = npcs[i]->getBody()->bottom();
		}
	}
	minX -= 50;
	maxX += 50;
	minY -= screenHeight;
	float infoBarHeight = screenHeight / 12;
	float backgroundHeight = screenHeight - infoBarHeight;
	minX -= screenWidth;
	maxX += screenWidth;
	if (ground){
		delete ground;
	}
	ground = new Platform(Vector2((minX + maxX) / 2, maxY + infoBarHeight / 2), Vector2(maxX - minX, infoBarHeight), "Ground");
	float sx = (maxX - minX) / (2 * screenWidth);
	float sy = 1.0f;
	Point2f intersectLeft = Point2f(minX, maxY - backgroundHeight * 2);
	Point2f intersectRight = Point2f(maxX, maxY - backgroundHeight * 2);
	Vertex2f_Texture ul(intersectLeft, Point2f(0.0f, 0.01f));
	Vertex2f_Texture ll(Point2f(minX, maxY), Point2f(0.0f, sy));
	Vertex2f_Texture lr(Point2f(maxX, maxY), Point2f(sx, sy));
	Vertex2f_Texture ur(intersectRight, Point2f(sx, 0.01f));
	background1 = Quadrilateral<Vertex2f_Texture>(ul, ll, lr, ur);
	background1.give_Material(new Material("Background1"));

	sy = (maxY - minY - backgroundHeight * 2) / backgroundHeight;
	Vertex2f_Texture ul2(Point2f(minX, minY), Point2f(0.0f, 0.01f));
	Vertex2f_Texture ll2(intersectLeft, Point2f(0.0f, sy));
	Vertex2f_Texture lr2(intersectRight, Point2f(sx, sy));
	Vertex2f_Texture ur2(Point2f(maxX, minY), Point2f(sx, 0.01f));
	background2 = Quadrilateral<Vertex2f_Texture>(ul2, ll2, lr2, ur2);
	background2.give_Material(new Material("Background2"));
}

void LevelEditor::on_key(const SDL_KeyboardEvent &event)
{
	Game &gr = get_Game();
	switch(event.keysym.sym){
		case SDLK_ESCAPE:
			gr.pop_state();
			break;
		case SDLK_UP:
			controls.up = (event.type == SDL_KEYDOWN);
			break;
		case SDLK_LEFT:
			controls.left = (event.type == SDL_KEYDOWN);
			break;
		case SDLK_DOWN:
			controls.down = (event.type == SDL_KEYDOWN);
			break;
		case SDLK_RIGHT:
			controls.right = (event.type == SDL_KEYDOWN);
			break;
		case SDLK_LCTRL:
		case SDLK_RCTRL:
			controlHeld = (event.type == SDL_KEYDOWN);
			break;
		case SDLK_F5:
			save();
			break;
		case SDLK_c:
			draw = "Civilian";
			break;
		case SDLK_e:
			draw = "Enemy";
			break;
		case SDLK_p:
			draw = "Platform";
			break;
		case SDLK_b:
			draw = "Boss";
			break;
		case SDLK_h:
			draw = "HealthPack";
			break;
	}
}

void LevelEditor::zoom(int dz)
{
	if (viewArea.first.x - dz < viewArea.second.x + dz){
		viewArea.first.x -= dz;
		viewArea.first.y -= dz * (screenHeight / screenWidth);
		viewArea.second.x += dz;
		viewArea.second.y += dz * (screenHeight / screenWidth);
	}
}

GameObject* LevelEditor::hoverObject(Vector2 mouse)
{
	//for testing collisions
	Platform *p = new Platform(mouse, Vector2(1, 1), "");
	for (int i = 0; i < platforms.size(); i++){
		if (Collision::objectsIntersect(p, platforms[i])){
			return platforms[i];
		}
	}
	for (int i = 0; i < npcs.size(); i++) {
		if (Collision::objectsIntersect(p, npcs[i]->getBody())){
			return npcs[i]->getBody();
		}
	}
	for (int i = 0; i < healthPacks.size(); i++) {
		if (Collision::objectsIntersect(p, healthPacks[i]))
			return healthPacks[i];
	}
	if (Collision::objectsIntersect(p, player->getBody())){
		return player->getBody();
	}
	return NULL;
}

void LevelEditor::on_mouse_motion(const SDL_MouseMotionEvent &event)
{	
	if (panning){
		Point2f offset2f = Point2f(event.x - mousePrev2f.x, event.y - mousePrev2f.y);
		offset2f.x *= (viewArea.second.x - viewArea.first.x) / 800;
		offset2f.y *= (viewArea.second.y - viewArea.first.y) / 600;
		mousePrev2f = Point2f(event.x, event.y);
		viewArea.first.x -= offset2f.x;
		viewArea.second.x -= offset2f.x;
		viewArea.first.y -= offset2f.y;
		viewArea.second.y -= offset2f.y;
	}
	else{
		Projector2D projector = Projector2D(viewArea);
		Point2f p = projector.unproject(Point2f(event.x, event.y));
		Vector2 mouse = Vector2(p.x, p.y);			
		if (moveObject){
			moveObject->setPos(moveObject->getPos() + Vector2(mouse - mousePrev));
			if (movingPlayer){
				movingPlayer->setExtrasInPosition();
			}
		}
		else if (newPlatform){
			newPlatform->setSecondPoint(mouse);
		}
		mousePrev = mouse;
	}
}

void LevelEditor::on_mouse_button(const SDL_MouseButtonEvent &event)
{
	Projector2D projector = Projector2D(viewArea);
	Point2f p = projector.unproject(Point2f(event.x, event.y));
	Vector2 mouse = Vector2(p.x, p.y);

	mouseDown = (event.type == SDL_MOUSEBUTTONDOWN);
	mousePrev = mouse;
	mousePrev2f = Point2f(event.x, event.y);

	switch (event.button){
		case SDL_BUTTON_LEFT:
			{
			GameObject *hoverObj = hoverObject(mouse);
			if (hoverObj){
				if (mouseDown){
					if (newPlatform){
						newPlatform = NULL;
					}
					if (controlHeld){
						bool found = false;
						for (int i = 0; i < platforms.size(); i++){
							if (platforms[i] == hoverObj){
								Platform *p = new Platform(*platforms[i]);
								hoverObj = p;
								platforms.push_back(p);
								movingPlayer = NULL;
								found = true;
								break;
							}
						}
						if (!found){
							for (int i = 0; i < npcs.size(); i++) {
								if (npcs[i]->getBody() == hoverObj){
									Player *p;
									if (npcs[i]->playertype == CIVILIAN){
										p = new Civilian(npcs[i]->getBody()->getPos());
									}
									else{
										p = new Enemy(npcs[i]->getBody()->getPos());
									}
									hoverObj = p->getBody();
									npcs.push_back(p);
									movingPlayer = p;
									found = true;
									break;
								}
							}
						}
					}
					else{
						//determine if moving player
						for (int i = 0; i < npcs.size(); i++) {
							if (npcs[i]->getBody() == hoverObj){
								movingPlayer = npcs[i];
								break;
							}
						}
					}
					moveObject = hoverObj;
				}
				else{
					moveObject = NULL;
				}
			}
			else{
				if (mouseDown){
					if (draw == "Civilian"){
						npcs.push_back(new Civilian(mouse));
					}
					else if (draw == "Enemy"){
						npcs.push_back(new Enemy(mouse));
					}
					else if (draw == "Boss") {
						npcs.push_back(new Boss(mouse));
					}
					else if (draw == "HealthPack") {
						healthPacks.push_back(new HealthPack(mouse));
					}
					else if (draw == "Platform"){
						if (!newPlatform){
							newPlatform = new Platform(mouse, draw);
							platforms.push_back(newPlatform);
						}
					}
				}
			}
			}
			break;
		case SDL_BUTTON_RIGHT:
			{
			if (event.type == SDL_MOUSEBUTTONDOWN){
				//remove object
				if (newPlatform){
					newPlatform = NULL;
					delete newPlatform;
					platforms.erase(platforms.end() - 1);
				}
				GameObject *hoverObj = hoverObject(mouse);
				if (hoverObj && hoverObj != player->getBody()){
					for (int i = 0; i < platforms.size(); i++){
						if (platforms[i] == hoverObj){
							delete platforms[i];
							platforms.erase(platforms.begin() + i);
							return;
						}
					}
					for (int i = 0; i < npcs.size(); i++) {
						if (npcs[i]->getBody() == hoverObj){
							delete npcs[i];
							npcs.erase(npcs.begin() + i);
							return;
						}
					}
					for (int i = 0; i < healthPacks.size(); i++) {
						if(healthPacks[i] == hoverObj) {
							delete healthPacks[i];
							healthPacks.erase(healthPacks.begin() + i);
							return;
						}
					}
				}
			}
			}
			break;
		case SDL_BUTTON_MIDDLE:
			panning = mouseDown;
			break;
		case SDL_BUTTON_WHEELUP:
			zoom(-25);
			break;
		case SDL_BUTTON_WHEELDOWN:
			zoom(25);
			break;
	}
}

void LevelEditor::perform_logic()
{
	static Time previous;
	Time current = get_Timer().get_time();
	float timestep = current.get_seconds_since(previous);
	previous = current;
	if (timestep > 0.1f){
		timestep = 0.1f;
	}
 
	timeSinceMove += timestep;
	if (timeSinceMove > 0.01f){
		timeSinceMove = 0;
		const float speed = (viewArea.second.x - viewArea.first.x) / 200;
		if (controls.left && !controls.right){
			viewArea.first.x -= speed;
			viewArea.second.x -= speed;
		}
		else if (controls.right && !controls.left){
			viewArea.first.x += speed;
			viewArea.second.x += speed;
		}
		if (controls.up && !controls.down){
			viewArea.first.y -= speed;
			viewArea.second.y -= speed;
		}
		else if (controls.down && !controls.up){
			viewArea.first.y += speed;
			viewArea.second.y += speed;
		}
	}
	determineBackground();
}

void LevelEditor::render()
{
	Video &vr = Zeni::get_Video();
	vr.set_2d(viewArea);
	vr.render(background2);
	vr.render(background1);
	ground->render();

	for (int i = 0; i < platforms.size(); i++){
		platforms[i]->render();
	}
	for (int i = 0; i < npcs.size(); i++){
		npcs[i]->render();
	}
	for (int i = 0; i < healthPacks.size(); i++) {
		healthPacks[i]->render();
	}
	player->render();

	vr.set_2d();
	const Font &font = get_Fonts()["titleButton"];
	font.render_text(draw, Point2f(10, 10), Color());
}